#include "Common.h"

SpellEffectHandler SpellHandler[162];

void Spell::InitTable()
{
	memset(SpellHandler, 0, sizeof(SpellEffectHandler) * 162);
	SpellHandler[5].effecthandler = &Spell::SpellEffectTeleport;
}

Spell::Spell(Player* caster, SpellEntry* ent, ui8 castnumber) : cast_number(castnumber), UnitTargetSet(false),
m_pGoCastedFrom(0)
{
	p_caster = caster;
	m_spellinfo = ent;
	if(!ent)
		return;
	casttime = dbcSpellCasts.LookupEntry(m_spellinfo->CastingTimeIndex)->casttime;
}

void Spell::Start()
{
	ui8 res = GetCastStatus();
	if(res != SPELL_CANCAST_OK)
		SendCastResult(res);
	else
		SendSpellStart();
}

void Spell::SendCastResult(ui8 res)
{
	RealmPacket data(SMSG_CAST_FAILED);
	switch(res)
	{
	case SPELL_FAILED_REQUIRES_AREA:
		{
			AreaGroup* ag = dbcAreaGroup.LookupEntry(m_spellinfo->RequiresAreaId);
			if(ag)
			{
				for(register ui32 i = 0; i < 7; ++i)
					if(p_caster->GetAreaId() != NO_AREA_RESULT && ag->AreaIds[i] && ag->AreaIds[i] != p_caster->GetAreaId())
					{
						data << cast_number << m_spellinfo->Id << res << ag->AreaIds[i];
						p_caster->GetSession()->SendPacket(&data);
						return;
					}
			}
			break;
		}
	}
	data << cast_number;
	if(m_spellinfo)
		data << m_spellinfo->Id;
	else
		data << ui32(1);
	data << res;
	p_caster->GetSession()->SendPacket(&data);
}

void Spell::Cancle(ui8 reason)
{
	if(!m_spellinfo)
		return;
	SendCastResult(39);
	RealmPacket data(SMSG_SPELL_FAILURE, 0);
	data << p_caster->GetNewGUID() << m_spellinfo->Id << reason;
	p_caster->GetSession()->SendPacket(&data);
	data.Clear();
	data.SetOpcode(SMSG_SPELL_FAILED_OTHER);
	data << p_caster->GetNewGUID() << m_spellinfo->Id;
	p_caster->GetSession()->SendToSet(&data);
	data.SetOpcode(MSG_CHANNEL_UPDATE);
	data << p_caster->GetNewGUID() << ui32(0);
	p_caster->GetSession()->SendToSet(&data, true);
	p_caster->RemoveEventsByType(EVENT_CAST_SPELL);
	p_caster->SetCurrentSpell(0);
}

ui8 Spell::GetCastStatus()
{
	if(!p_caster || !m_spellinfo)
		return SPELL_FAILED_ERROR;
	SpellEntry* sp = m_spellinfo;
	if(sp->RequiresAreaId)
	{
		AreaGroup* group = dbcAreaGroup.LookupEntry(sp->RequiresAreaId);
		if(group)
		{
			for(register ui32 i = 0; i < 7; ++i)
			{
				if(group->AreaIds[i] && p_caster->GetAreaId() != NO_AREA_RESULT && p_caster->GetAreaId() != group->AreaIds[i])
					return SPELL_FAILED_REQUIRES_AREA;
			}
		}
	}
	for(register ui32 i = 0; i < 2; ++i)
	{
		if(m_spellinfo->Totem[i] && !p_caster->GetItemInterface()->GetItemCount(m_spellinfo->Totem[i]))
			return SPELL_FAILED_TOTEMS;
	}
	return SPELL_CANCAST_OK;
}

void Spell::SetUnitTarget(ui64 guid)
{
	if(guid == p_caster->GetGUID())
	{
		targets.targetMask[0] = 0;
		targets.targetUnit = guid;
		return;
	}
}

void Spell::FillTargets(ui64 guid, RealmPacket* source)
{
	(*source) >> targets.targetMask[0] >> targets.targetMask[1];
	WoWGuid tmpguid;
	if(targets.targetMask[0] == 0)
	{
		targets.targetUnit = guid;
		return;
	}

	if(targets.targetMask[0] & (2 | 512 | 2048 | 32768))
	{
		(*source) >> tmpguid;
		targets.targetUnit = tmpguid.GetOldGuid();
	}

	if(targets.targetMask[0] & (16 | 4096))
	{
		(*source) >> tmpguid;
		targets.targetItem = tmpguid.GetOldGuid();
	}

	if(targets.targetMask[0] & 32)
	{
		(*source) >> targets.srcX >> targets.srcY >> targets.srcZ;
		if(!(targets.targetMask[0] & 64))
		{
			targets.destX = targets.srcX;
			targets.destY = targets.srcY;
			targets.destZ = targets.srcZ;
		}
	}

	if(targets.targetMask[0] & 64)
	{
		(*source) >> targets.destX >> targets.destY >> targets.destZ;
		if(!(targets.targetMask[0] & 32))
		{
			targets.srcX = targets.destX;
			targets.srcY = targets.destY;
			targets.srcZ = targets.destZ;
		}
	}

	if(targets.targetMask[0] & 8192)
	{
		(*source) >> (char*)targets.stringTarget;
	}
}

void Spell::SendSpellStart()
{
	RealmPacket ddata(SMSG_SPELL_START);
	ddata << p_caster->GetNewGUID() << p_caster->GetNewGUID();
	ddata << cast_number << m_spellinfo->Id;
	ddata << ui32(0) << casttime;
	switch(targets.targetMask[0])
	{
	case 0:
		ddata << targets.targetMask[0];
		break;
	case 2:
		ddata << targets.targetMask[0] << targets.targetUnit;
		break;
	}
	p_caster->GetSession()->SendToSet(&ddata, true);
	TimedEvent* te = new TimedEvent(new CallbackP0<Spell>(&Spell::SendSpellGo, this), casttime, 1, EVENT_CAST_SPELL);
	p_caster->AddEvent(te);
	p_caster->SetCurrentSpell(this);
}

void Spell::SendSpellGo()
{
	p_caster->SetCurrentSpell(0);
	for(register ui8 i = 0; i < 3; ++i)
		if(m_spellinfo->Effect[i] == 6)
		{
			RealmPacket data(SMSG_AURACASTLOG);
			data << p_caster->GetGUID();
			data << targets.targetUnit;
			data << m_spellinfo->Id;
			data << ui64(0);
			p_caster->GetSession()->SendToSet(&data, true);
			data.Clear();
			data.SetOpcode(SMSG_AURA_UPDATE);
			p_caster->PackGuid(&data);
			data << ui8(0) << m_spellinfo->Id;
			data << ui16(0x1d39) << ui8(1);
			data << ui32(10000) << ui32(10000);
			p_caster->GetSession()->SendToSet(&data, true);
		}
	RealmPacket data(SMSG_SPELL_GO);
	data << p_caster->GetNewGUID() << p_caster->GetNewGUID();
	data << cast_number << m_spellinfo->Id;
	data << ui32(0) << ui32(GetTickCount()) << ui8(1);
	data << targets.targetUnit;
	switch(targets.targetMask[0])
	{
	case 0:
		data << targets.targetMask[0] << targets.targetMask[1];
		break;
	case 2:
		data << targets.targetMask[0] << targets.targetMask[1] << targets.targetUnit;
		break;
	}
	p_caster->GetSession()->SendToSet(&data, true);
	if(m_pGoCastedFrom)
	{
		GameObjectTeleport* gTeleport = miscmgr->GetGameObjectTeleport(m_pGoCastedFrom->GetEntry());
		if(gTeleport)
		{
			p_caster->TeleportTo(gTeleport->x, gTeleport->y, gTeleport->z, gTeleport->mapid);
			return;
		}
	}
	SpellEffectHandler hndl = SpellHandler[m_spellinfo->Effect[0]];
	if(hndl.effecthandler)
	{
		(this->*hndl.effecthandler)();
	}
}

void Spell::SpellEffectTeleport()
{
	p_caster->TeleportTo(p_caster->GetBindPoint());
}

void Session::HandleCancelCast(RealmPacket& /* data */)
{
	if(m_player->m_currentspell)
		m_player->m_currentspell->Cancle(0);
}